//
// Created by Scave on 2025/8/29.
//

#include "InstancingRenderSystem.h"
#include "bgfx/bgfx.h"
#include "bx/math.h"
#include "EntityUtil.h"
#include "DataUtil.h"
#include "component/InternalComponent.h"

NS_RIDER_BEGIN

    void InstancingRenderSystem::configure(EntityManager& entities, EventManager& events) {

    }

    void InstancingRenderSystem::update(EntityManager& entities, EventManager& events, TimeDelta dt) {
        std::vector<LightItem> lights;
        CollectLights(entities, lights);
        Entity root_entity = EntityUtil::FindRootEntity(entities);
        ComponentHandle<RenderNodes> nodes = root_entity.component<RenderNodes>();
        Camera* camera = nullptr;
        if (root_entity.has_component<Camera>()) {
            camera = root_entity.component<Camera>().get();
        }
        for (auto entity: entities.entities_with_components<InstancingRenderModel>()) {
            ComponentHandle<InstancingRenderModel> model = entity.component<InstancingRenderModel>();
            SMART_PTR<InstancingRenderNode> node = MAKE_SMART_PTR<InstancingRenderNode>();
            node->name = entity.component<EntityIdentifier>()->name;
            node->lights = lights;
            node->mesh = model->mesh;
            node->material = model->material;
            node->ambient_color = root_entity.component<SceneConfig>()->ambient_color;
            if (camera != nullptr) {
                node->camera_pos = camera->position;
            }
            node->model_matrices = DataUtil::Convert<Transform, Mat4>(model->model_transforms, [](const Transform& transform) {
                return Mat4{};
            });
            nodes->PushNode(LayerMask::Object, node);
        }
        nodes->SetLayerCamera(LayerMask::Object,
                              ComputeViewMatrix(camera), ComputeProjectionMatrix(camera));
    }

    Mat4 InstancingRenderSystem::ComputeViewMatrix(Camera* camera) {
        if (camera == nullptr) {
            return {};
        }
        bx::Vec3 eye = {camera->position.x, camera->position.y, camera->position.z};
        bx::Vec3 at = {camera->target.x, camera->target.y, camera->target.z};
        bx::Vec3 up_axis = {camera->up_axis.x, camera->up_axis.y, camera->up_axis.z};
        float* mtx = new float[16];
        bx::mtxLookAt(mtx, eye, at, up_axis, bx::Handedness::Right);
        return Mat4(mtx);
    }

    Mat4 InstancingRenderSystem::ComputeProjectionMatrix(Camera* camera) {
        if (camera == nullptr) {
            return {};
        }
        float* mtx = new float[16];
        if (camera->projection_type == ProjectionType::Perspective) {
            bx::mtxProj(mtx,
                        camera->field_of_view,
                        camera->view_port.w * 1.f / camera->view_port.h,
                        camera->near_clip_plane, camera->far_clip_plane,
                        bgfx::getCaps()->homogeneousDepth,
                        bx::Handedness::Right
            );
        } else {
            bx::mtxOrtho(mtx, 0, 0,
                         camera->orthographic_size, camera->orthographic_size,
                         camera->near_clip_plane, camera->far_clip_plane,
                         0, bgfx::getCaps()->homogeneousDepth,
                         bx::Handedness::Right);
        }
        return Mat4(mtx);
    }

    void InstancingRenderSystem::CollectLights(EntityManager& entities, std::vector<LightItem>& lights) {
        for (Entity entity: entities.entities_with_components<Light>()) {
            LightItem light;
            light.type = entity.component<Light>()->type;
            light.strength = entity.component<Light>()->strength;
            light.position = entity.component<Transform>()->translation;

            Quaternion q = entity.component<Transform>()->rotation;
            light.direction = q.ToDirection();

            light.color = entity.component<Light>()->color;
            light.range = entity.component<Light>()->range;
            light.attenuation = entity.component<Light>()->attenuation;
            light.spotAngle = entity.component<Light>()->spotAngle;

            lights.push_back(light);
        }
    }

NS_RIDER_END